#pragma once

#include "actor.h"

class TestMessageHandling
{
	class ReceiverReplica
	{
	public:
		ReceiverReplica ()
		: msgCount ( 0 )
		{
		}
		int msgCount;
	};
	
	class Receiver
	: public Actor < Receiver, ReceiverReplica >
	, public ReceiverReplica
	{
	public:
		virtual void UpdateReplica ( ReceiverReplica & replica ) const
		{
			replica = * this;
		}
	};
	
	class ReceiversFactory
	: public ActorsFactory < Receiver, ReceiverReplica >
	{
	public:
		Id Create ()
		{
			return RegisterNewActor ( new Receiver () );
		}
	};
	
	class IncreaseCount
	: public Message < Receiver, ReceiverReplica >
	{
		IncreaseCount ()
		{
			TestMessageHandling::messagesCreated++;
		}
		virtual ~IncreaseCount ()
		{
			TestMessageHandling::messagesDeleted++;
		}
		virtual void Execute ( Receiver & receiver )
		{
			receiver.msgCount += 1;
		}
	public:
		static void SendTo ( const Receiver::Id & receiverId )
		{
			SendNewMessage ( receiverId, new IncreaseCount () );
		}
	};
	
	class RecursiveCount
	: public Message < Receiver, ReceiverReplica >
	{
		virtual void Execute ( Receiver & receiver )
		{
			if ( receiver.msgCount < 100 )
			{
				RecursiveCount::SendTo ( receiver.GetId () );
				receiver.msgCount++;
			}
		}
	public:
		static void SendTo ( const Receiver::Id & receiverId )
		{
			SendNewMessage ( receiverId, new RecursiveCount () );
		}
	};
	
	static void TestHandleNoMessages ()
	{
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		const ReceiverReplica & receiver = * id.GetReplica ();
		factory.HandleMessages ();
		factory.UpdateReplicas ();
		assert ( receiver.msgCount == 0 );		
	}

	static void TestMessagesBuffering ()
	{
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		const ReceiverReplica & receiver = * id.GetReplica ();
		IncreaseCount::SendTo ( id );
		factory.UpdateReplicas ();
		assert ( receiver.msgCount == 0 );
	}

	static void TestHandleThreeMessages ()
	{
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		const ReceiverReplica & receiver = * id.GetReplica ();
		IncreaseCount::SendTo ( id );
		IncreaseCount::SendTo ( id );
		IncreaseCount::SendTo ( id );
		factory.HandleMessages ();
		factory.UpdateReplicas ();
		assert ( receiver.msgCount == 3 );
	}
	
	static void TestEmptyMessagesBufferAfterHandling ()
	{
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		const ReceiverReplica & receiver = * id.GetReplica ();
		IncreaseCount::SendTo ( id );
		factory.HandleMessages ();
		factory.UpdateReplicas ();
		factory.HandleMessages ();
		factory.UpdateReplicas ();
		assert ( receiver.msgCount == 1 );
	}
	
	static void TestRecursiveMessages ()
	{
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		const ReceiverReplica & receiver = * id.GetReplica ();
		RecursiveCount::SendTo ( id );
		factory.HandleMessages ();
		factory.UpdateReplicas ();
		assert ( receiver.msgCount == 1 );
		factory.HandleMessages ();
		factory.UpdateReplicas ();
		assert ( receiver.msgCount == 2 );
	}
	
	static void TestMessageSendLifeTime ()
	{
		messagesCreated = 0;
		messagesDeleted = 0;
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		IncreaseCount::SendTo ( id );
		IncreaseCount::SendTo ( id );
		IncreaseCount::SendTo ( id );
		assert ( messagesCreated == 3 );
		assert ( messagesDeleted == 0 );
	}

	static void TestHandleMessagesLifeTime ()
	{
		messagesCreated = 0;
		messagesDeleted = 0;
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		IncreaseCount::SendTo ( id );
		IncreaseCount::SendTo ( id );
		IncreaseCount::SendTo ( id );
		factory.HandleMessages ();
		assert ( messagesCreated == 3 );
		assert ( messagesDeleted == 3 );
	}
	
	static void TestDeleteMessagesInBufferAfterActorDestruction ()
	{
		messagesDeleted = 0;
		{
			ReceiversFactory factory;
			Receiver::Id id = factory.Create ();
			IncreaseCount::SendTo ( id );
			IncreaseCount::SendTo ( id );
			IncreaseCount::SendTo ( id );
		}
		assert ( messagesDeleted == 3 );
	}
	
	static void TestPassMessageToDeletedActor ()
	{
		ReceiversFactory factory;
		Receiver::Id id = factory.Create ();
		id.Delete ();
		IncreaseCount::SendTo ( id );
	}
	
public:	
	static void Run ()
	{
		TestHandleNoMessages ();
		TestMessagesBuffering ();
		TestHandleThreeMessages ();
		TestEmptyMessagesBufferAfterHandling ();
		TestRecursiveMessages ();
		TestMessageSendLifeTime ();
		TestHandleMessagesLifeTime ();
		TestDeleteMessagesInBufferAfterActorDestruction ();
		TestPassMessageToDeletedActor ();
	}
	static int messagesCreated;
	static int messagesDeleted;
};
int TestMessageHandling::messagesCreated = 0;
int TestMessageHandling::messagesDeleted = 0;

